package cn.lg.soar.core.manager;

import cn.lg.soar.common.util.PathMatcher;
import cn.lg.soar.common.util.hardware.SystemUtils;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 权限级别管理器
 * @author luguoxiang
 * @date 2022/4/4
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class PermitLevelManager {

    protected static final Logger LOGGER = LoggerFactory.getLogger(PermitLevelManager.class);

    public final static int OTHER = -1;

    /**
     * 缓存权限级别匹配结果
     */
    private final Cache<String, Integer> cache;

    /**
     * 权限级别配置
     */
    private final Map<Integer, String[]> levelMap = new ConcurrentHashMap<>();
    private volatile Integer[] integers;

    public PermitLevelManager(Cache<String, Integer> cache) {
        this.cache = cache;
    }

    public PermitLevelManager() {
        long cacheSize = SystemUtils.TOTAL >>> 16;
        cache = Caffeine.newBuilder()
                .maximumSize(cacheSize)
                .initialCapacity(64)
                .expireAfterAccess(5, TimeUnit.MINUTES)
                .build();
        LOGGER.info("permit-level cache max size {}", cacheSize);
    }

    /**
     * 新增或跟新权限级别（已存在则更新，不存在则添加）
     * @param level
     * @param patterns
     */
    public void putLevel(int level, Collection<String> patterns) {
        Assert.isTrue(level >= 0, "level cannot be less than 0");
        Assert.notNull(patterns, "patterns cannot null");
        if (patterns.isEmpty()) {
            return;
        }
        String[] strings = patterns.stream().distinct().toArray(String[]::new);
        levelMap.put(level, strings);
        integers = levelMap.keySet().stream().sorted().toArray(Integer[]::new);
        cache.invalidateAll();
    }

    /**
     * 匹配权限级别
     * @param permit
     * @return
     */
    public int match(String permit) {
        // 小于250的路径才缓存，以免内存过大
        if (permit.length() < 250) {
            // 通过缓存匹配
            return cache.get(permit, this::innerMatch);
        }
        // 直接匹配
        return innerMatch(permit);
    }

    /**
     * 匹配权限级别
     * @param permit
     * @return
     */
    private int innerMatch(String permit) {
        for (Integer level : integers) {
            String[] strings = levelMap.get(level);
            if (PathMatcher.matchAny(permit, strings)) {
                return level;
            }
        }
        return OTHER;
    }

}
